using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Collections.Generic;
using System.Drawing.Design;
using System.Diagnostics;
using System.Text;
using System.IO;

using InfFront.UICore;

namespace InfFront.InfoSources.Base
{
    /// <summary>
    /// Base IInfoSource implementation
    /// </summary>
    [ToolboxItem(false), DesignTimeVisible(false),
     Designer("InfFront.InfoSources.Base.Design.InfoSourceDesigner, InfFront.InfoSources.Base.Design", typeof(System.ComponentModel.Design.IRootDesigner)),
     DesignerSerializer("InfFront.InfoSources.Base.Design.InfoSourceRootCodeDomSerializer, InfFront.InfoSources.Base.Design", typeof(TypeCodeDomSerializer))]
    public abstract partial class InfoSource : Component, IInfoSource, IInfoSourceOwnedObject
    {
        #region Private Members
        private InfoSourceList sources;
        private InfoFieldList fields;
        private InfoFieldGroupList fieldGroups;
        private InfoCommandList commands;
        private List<IInfoSourceCallbacks> callbacks;
        private InfoSource owner;
        private string title = string.Empty;
        private string name = string.Empty;
        private bool enabled = true;
        private bool isDummy = false;
        private IconStack icons;
        private CallbackMessageBatch cBatch = null;
        private int order = 99;
        private InfoCommand listDoubleClickCommand = null;
        private bool allowsInplaceEdit = false;
        private bool needsEditBuffer = true;
        #endregion

        #region Construction/Initialization
        public InfoSource()
        {
            Init();
        }

        public InfoSource(IContainer container)
        {
            container.Add(this);
            Init();
        }

        private void Init()
        {
            sources = new InfoSourceList(this);
            fields = new InfoFieldList(this);
            fieldGroups = new InfoFieldGroupList(this);
            commands = new InfoCommandList(this);
            callbacks = new List<IInfoSourceCallbacks>();
            icons = new IconStack();

            InitializeComponent();
        }

        #endregion

        #region Public Properties

        [Browsable(false)]
        public InfoSource Owner
        {
            get { return owner; }
        }

        void IInfoSourceOwnedObject.SetOwner(InfoSource value)
        {
            owner = value;
        }

        [Category("General"), DefaultValue(true), Description("If the InfoSource is enabled")]
        public bool IsEnabled
        {
            get { return enabled; }
            set { enabled = value; }
        }

        [Category("General"), DefaultValue(false), Description("If the InfoSource used only as titled tree node and doesn't have it's own data")]
        public bool IsDummy
        {
            get { return isDummy; }
            set { isDummy = value; }
        }

        [Category("General"), DefaultValue(99), Browsable(false), Description("InfoSource's list order")]
        public int Order
        {
            get { return order; }
            set { order = value; }
        }

        [Category("General"), DefaultValue(false), Description("If the inplace (in a grid cell, for instance) entry editing is allowed")]
        public bool AllowsInplaceEdit
        {
            get { return allowsInplaceEdit; }
            set { allowsInplaceEdit = value; }
        }

        [Category("General"), DefaultValue(true), Description("If editing of the InfoSource entries must be buffered (with Cancel/Apply buttons)")]
        public bool NeedsEditBuffer
        {
            get { return needsEditBuffer; }
            set { needsEditBuffer = value; }
        }

        [Category("General"), Editor("InfFront.InfoSources.Base.Design.IconStackEditor, InfFront.InfoSources.Base.Design", typeof(UITypeEditor)), MergableProperty(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Description("InfoSource's icons")]
        public IconStack Icons
        {
            get { return icons; }
            set { icons = value; }
        }

        [Browsable(false)]
        public string Name
        {
            get
            {
                string result = name;
                if (string.IsNullOrEmpty(result) && Site != null)
                    result = Site.Name;
                if (string.IsNullOrEmpty(result))
                    result = GetType().FullName;
                return result;
            }
            set
            {
                name = value;
                if (name == null)
                    name = string.Empty;
            }
        }

        [Category("General"), DefaultValue(""), Description("InfoSource's title")]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        [Category("General"), DefaultValue(null), Description("Command to be executed when user doubleclicks a grid row")]
        public InfoCommand ListDoubleClickCommand
        {
            get { return listDoubleClickCommand; }
            set { listDoubleClickCommand = value; }
        }


        [Category("General"), DefaultValue(null), Description("the field which is to be used to retrieve record icons at the ListView/Icon List modes")]
        public InfoField IconField
        {
            get;
            set;
        }

        [Category("General"), DefaultValue(null), Description("the field which is to be used to retrieve record titles at the ListView/Icon List modes")]
        public InfoField TitleField
        {
            get;
            set;
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public InfoSourceList NestedSources
        {
            get { return sources; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public InfoFieldList Fields
        {
            get { return fields; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public InfoFieldGroupList FieldGroups
        {
            get { return fieldGroups; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public InfoCommandList Commands
        {
            get { return commands; }
        }

        #endregion Public Properties

        #region IInfoSource Members

        string IInfoSource.GetName()
        {
            return Name;
        }

        string IInfoSource.GetTitle()
        {
            return Title;
        }

        bool IInfoSource.GetIsDummy()
        {
            return IsDummy;
        }

        protected abstract int GetMaxEntryCount();

        int IInfoSource.GetMaxEntryCount()
        {
            int rv = this.GetMaxEntryCount();
            if (rv == -1 && Fields.Count == 0)
                rv = 0;
            return rv;
        }

        string IInfoSource.GetListDoubleClickCommandName()
        {
            if (ListDoubleClickCommand != null)
                return ListDoubleClickCommand.Name;
            return null;
        }

        bool IInfoSource.GetAllowsInplaceEdit()
        {
            return AllowsInplaceEdit;
        }

        bool IInfoSource.GetNeedsEditBuffer()
        {
            return NeedsEditBuffer;
        }

        protected virtual bool SupportsSorting
        {
            get { return false; }
        }

        bool IInfoSource.GetSupportsSorting()
        {
            return SupportsSorting;
        }

        IInfoSourceList IInfoSource.GetChildSources()
        {
            FireBeforeGetNestedSources();
            var result = new InfoSourceList.InterfaceContainer();
            foreach (InfoSource item in sources)
                if (item.IsEnabled)
                    result.Add(item);
            return result;
        }

        IInfoFieldList IInfoSource.GetFields()
        {
            var result = new InfoFieldList.InterfaceContainer();
            foreach (InfoField item in fields)
                if (item.Enabled)
                    result.Add(item);
            return result;
        }

        IInfoCommandList IInfoSource.GetCommands()
        {
            var result = new InfoCommandList.InterfaceContainer();
            foreach (InfoCommand item in commands)
                if (item.Enabled)
                    result.Add(item);
            return result;
        }

        protected abstract IInfoEntries GetEntries(int maxCount, EntryFilters filters, EntrySortDescriptions sorts);
        protected internal abstract object UnwrapEntry(IInfoEntry entry);
        protected abstract IInfoEntry CreateEmptyEntry();

        protected IInfoEntries GetEntries()
        {
            return GetEntries(-1, null, null);
        }

        IInfoEntries IInfoSource.GetEntries(int maxCount, string[] filterFieldNames, object[] filterValues,
                                            string[] sortFieldNames, ListSortDirection[] sortFieldDirections)
        {
            EntryFilters filters = null;
            if (filterFieldNames != null)
            {
                Debug.Assert(filterFieldNames.Length == filterValues.Length);
                filters = new EntryFilters(filterFieldNames.Length);
                for (int i = 0; i < filterFieldNames.Length; ++i)
                    filters.Add(new EntryFilter(Fields[filterFieldNames[i]], filterValues[i]));
            }

            EntrySortDescriptions sorts = null;
            if (sortFieldNames != null)
            {
                Debug.Assert(sortFieldDirections.Length == sortFieldNames.Length);
                sorts = new EntrySortDescriptions(sortFieldNames.Length);
                for (int i = 0; i < sortFieldNames.Length; ++i)
                    sorts.Add(new EntrySortDescription(Fields[sortFieldNames[i]], sortFieldDirections[i]));
            }

            return this.GetEntries(maxCount, filters, sorts);
        }

        void IInfoSource.SetCallbacks(IInfoSourceSetCallbacksArgs args)
        {
            var action = args.GetAction();
            if (action == SourceSetCallbacksActionKind.Register)
                callbacks.Add(args.GetCallbacks());
            else if (action == SourceSetCallbacksActionKind.Unregister)
                callbacks.Remove(args.GetCallbacks());
            else
                throw new NotSupportedException("Unsupported ActionKind");
        }

        private Stream GetPngStream(int hSize, int vSize, IconStyle style, IInfoEntry entry, string fieldName)
        {
            if (entry == null)
            {
                Debug.Assert(string.IsNullOrEmpty(fieldName));
                return Icons.GetStretched(hSize, vSize, style);
            }
            InfoField field = Fields[fieldName];
            var obj = UnwrapEntry(entry);
            try
            {
                var data = field.GetValue(obj);
                if (data == null)
                    return null;
                return ((ImageDataType)field.DataTypeParams).ConvertToPngStream(data, hSize, vSize, style);
            }
            catch (Exception ex)
            {
                throw new InfoFieldException(field, string.Format("Error reading image from the '{0}' field.", field.Name), ex);
            }
        }

        IImage IInfoSource.GetImage(int hSize, int vSize, IconStyle style, IInfoEntry entry, string fieldName)
        {
            var stream = GetPngStream(hSize, vSize, style, entry, fieldName);
            // TODO: implement the UID generation here, and at the BaseCommand.GetImage. Support IImage chaching on the InfoViews.Base side
            return new ImageRecord() { Data = stream };
        }

        IExecutionControl IInfoSource.ExecuteCommand(IExecuteCommandArgs args)
        {
            InfoCommand cmd = (InfoCommand)args.GetCommand();
            if (cmd.Owner != this)
                throw new ArgumentException("The Command belongs to other InfoSource", "Command");

            return cmd.StartExecution(args);
        }

        bool[] IInfoSource.CheckCommandsEnabled(IInfoEntry currentEntry, string[] commandNames)
        {
            var result = new bool[commandNames.Length];
            var entry = UnwrapEntry(currentEntry);
            for (int i = 0; i < result.Length; ++i)
            {
                var cmd = Commands[commandNames[i]];
                result[i] = cmd.CheckEnabled(entry);
            }
            return result;
        }

        internal static IEnumerable<IDataTypeTraits> GetFieldDataTypeTraits(IEnumerable<InfoField> fields)
        {
            foreach (var field in fields)
                yield return field.DataTypeTraits;
        }

        internal static IEnumerator<object> GetFieldDataValues(IEnumerable<InfoField> fields, object obj)
        {
            foreach (var field in fields)
                yield return field.GetValue(obj);
        }

        internal static IEnumerator<object> GetDefaultFieldDataValues(IEnumerable<InfoField> fields)
        {
            foreach (var field in fields)
                yield return field.DataTypeParams.GetDefaultValue();
        }

        internal IList<InfoField> FieldNameArrayToFields(string[] fieldNames)
        {
            List<InfoField> fields = null;
            if (fieldNames == null)
                fields = Fields;
            else
            {
                fields = new List<InfoField>(fieldNames.Length);
                foreach (string fname in fieldNames)
                {
                    InfoField field = Fields[fname];
                    fields.Add(field);
                }
            }
            return fields;
        }

        Stream IInfoSource.GetData(IInfoEntry entry, string[] fieldNames)
        {
            var obj = UnwrapEntry(entry);

            IList<InfoField> fields = FieldNameArrayToFields(fieldNames);

            var packer = new FieldDataPacker(GetFieldDataTypeTraits(fields));
            // TODO: Dont use the MemoryStream here. Implement effective proxy stream instead.
            var stream = new MemoryStream();
            var writer = new BinaryWriter(stream);

            if (obj != null)
                packer.Pack(writer, GetFieldDataValues(fields, obj)); // TODO: redesign that to make all the field data packing exceptions to appropriately catched and rethrown as InfoFieldExceptons
            else
                packer.Pack(writer, GetDefaultFieldDataValues(fields));

            stream.Seek(0, SeekOrigin.Begin);

            return stream;
        }

        void IInfoSource.SetData(IInfoEntry entry, string[] fieldNames, Stream data)
        {
            var obj = UnwrapEntry(entry);
            if (obj == null)
            {
                // did you forget to implement the OnCreateEmptyItem event handler?
                throw new ArgumentException("No object found");
            }

            IList<InfoField> fields = FieldNameArrayToFields(fieldNames);

            using (data)
            {
                var unpacker = new FieldDataUnpacker(GetFieldDataTypeTraits(fields));
                var reader = new BinaryReader(data);
                var values = unpacker.Unpack(reader);
                if (values != null) // the stream is empty
                {
                    foreach (InfoField field in fields)
                    {
                        values.MoveNext();
                        field.SetValue(obj, values.Current);
                    }
                    FireAfterEntryChange(obj, entry, fields);
                    BeginRefresh(entry);
                }
            }
        }

        private InfoField FindFirstFieldByKind(BaseDataType.Type kind)
        {
            InfoField rv = null;
            foreach (var fld in Fields)
            {
                if (fld.DataTypeKind == kind && fld.Enabled)
                {
                    if (rv == null || fld.Order < rv.Order)
                        rv = fld;
                }
            }
            return rv;
        }

        string IInfoSource.GetIconFieldName()
        {
            var field = IconField;
            if (field == null)
                field = FindFirstFieldByKind(BaseDataType.Type.Image);
            else if (field.DataTypeKind != BaseDataType.Type.Image)
                throw new ArgumentException(string.Format("Wrong '{0}' field parameter: {1}. It must be 'Image'!", field.Name, field.DataTypeKind));
            if (field != null)
                return field.Name;
            return null;
        }

        string IInfoSource.GetTitleFieldName()
        {
            var field = TitleField;
            if (field == null)
                field = FindFirstFieldByKind(BaseDataType.Type.String);
            else if (field.DataTypeKind != BaseDataType.Type.String)
                throw new ArgumentException(string.Format("Wrong '{0}' field parameter: {1}. It must be 'String'!", field.Name, field.DataTypeKind));
            if (field != null)
                return field.Name;
            return null;
        }
        #endregion

        public class FindArgs
        {
            public string FieldName { get; set; }
            public object FieldValue { get; set; }
        }

        public virtual IEnumerable<IInfoEntry> Find(FindArgs args)
        {
            var filters = new EntryFilters(1);
            filters.Add(new EntryFilter(Fields[args.FieldName], args.FieldValue));
            return GetEntries(-1, filters, null);
        }

        public virtual bool HasEntries()
        {
            var entries = GetEntries();
            return (entries != null) && entries.GetCount() > 0;
        }

        private static readonly object EVENT_BEFORE_GETNESTEDSOURCES = new object();

        public event EventHandler BeforeGetNestedSources
        {
            add { Events.AddHandler(EVENT_BEFORE_GETNESTEDSOURCES, value); }
            remove { Events.RemoveHandler(EVENT_BEFORE_GETNESTEDSOURCES, value); }
        }

        private static readonly object EVENT_AFTER_ENTRYCHANGE = new object();

        public event AfterEntryChangeHandler AfterEntryChange
        {
            add { Events.AddHandler(EVENT_AFTER_ENTRYCHANGE, value); }
            remove { Events.RemoveHandler(EVENT_AFTER_ENTRYCHANGE, value); }
        }

        public override ISite Site
        {
            get
            {
                return base.Site;
            }
            set
            {
                base.Site = value;
                if (DesignMode)
                {
                    // Derived icons and aggregated InfoSource icons must not be shown and reserialized.
                    // TODO: find a way to move this hack to the InfoSourceDesigner.
                    Icons.Clear();
                }
            }
        }

        #region Internal Members

        [Browsable(false)]
        internal InfoSource RootOwner
        {
            get { return (owner != null)? owner.RootOwner: this; }
        }

        public bool IsChildOf(InfoSource src)
        {
            if (src == Owner)
                return true;
            if (Owner != null)
                return Owner.IsChildOf(src);
            return false;
        }

        internal IEnumerable<IInfoSourceCallbacks> Callbacks
        {
            get { return callbacks; }
        }

        internal bool IsBeingDesigned
        {
            get
            {
                // TODO: find out why the DesignMode returns false here during several InfoFieldGroup creation
                return true; // DesignMode;
            }
        }

        internal void DispatchMessage(ICallbackMessage message)
        {
            if (cBatch == null)
                message.Proceed();
            else
                cBatch.Add(message);
        }

        internal CallbackMessageBatch SetMessageBatch(CallbackMessageBatch newBatch)
        {
            var prev = cBatch;
            cBatch = newBatch;
            return prev;
        }

        #endregion Internal Members

        #region Protected Members

        protected bool HasCallbacks
        {
            get { return callbacks.Count > 0; }
        }

        /// <summary>
        /// Refresh all associated views
        /// </summary>
        /// <param name="entry">the entry to refresh or null to refresh everything</param>
        public virtual void BeginRefresh(IInfoEntry entry)
        {
            RootOwner.DispatchMessage(new InfoSourceEntryRefreshMessage() { Source = this, Entry = entry });
        }

        /// <summary>
        /// Completely rebuild all associated views
        /// </summary>
        protected virtual void BeginRebuild()
        {
            // TODO: consider makind this method internal. 
            // Users probably shouldn't manually call it. 
            RootOwner.DispatchMessage(new InfoSourceRebuildMessage() { Source = this });
        }

        /// <summary>
        /// Refresh all associated views
        /// </summary>
        public virtual void BeginRefresh()
        {
            BeginRefresh(null);
        }

        public void BeginEdit(IInfoEntry entry)
        {
            if (this.IsDummy)
                throw new NotSupportedException("Dummy InfoSources don't support any object editing");
            RootOwner.DispatchMessage(new InfoSourceEditEntryMessage() { Source = this, Entry = entry });
        }

        public void BeginCreate()
        {
            if (this.IsDummy)
                throw new NotSupportedException("Dummy InfoSources don't support new record creation");
            RootOwner.DispatchMessage(new InfoSourceCreateEntryMessage() { Source = this, Entry = CreateEmptyEntry() });
        }

        protected virtual void FireBeforeGetNestedSources()
        {
            var handler = (EventHandler)Events[EVENT_BEFORE_GETNESTEDSOURCES];
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        protected virtual void FireAfterEntryChange(object obj, IInfoEntry entry, IEnumerable<InfoField> changedFields)
        {
            var handler = (AfterEntryChangeHandler)Events[EVENT_AFTER_ENTRYCHANGE];
            if (handler != null)
            {
                var args = new AfterEntryChangeArgs();
                args.Entry = entry;
                args.Object = obj;
                args.ChangedFields = changedFields;
                handler(this, args);
            }
        }
        
        #endregion
    }

    /// <summary>
    /// AfterEntryChangeHandler arguments
    /// </summary>
    public struct AfterEntryChangeArgs
    {
        public object Object { get; set; }

        public IInfoEntry Entry { get; set; }

        public IEnumerable<InfoField> ChangedFields { get; set; }
    }

    public delegate void AfterEntryChangeHandler(object sender, AfterEntryChangeArgs args);

    /// <summary>
    /// Base IInfoSourceList implementation
    /// </summary>
    public class InfoSourceList : InfoSourceOwnedListOf<InfoSource>
    {
        public InfoSourceList(InfoSource owner)
            : base(owner)
        {
        }

        public class InterfaceContainer : Container<IInfoSource>, IInfoSourceList
        {
        }
    }

    /// <summary>
    /// InfoSource entry sort description
    /// </summary>
    public class EntrySortDescription
    {
        private InfoField field;
        private ListSortDirection direction;

        public EntrySortDescription(InfoField field, ListSortDirection direction)
        {
            this.field = field;
            this.direction = direction;
        }

        public InfoField Field
        {
            get { return field; }
        }

        public ListSortDirection Direction
        {
            get { return direction; }
        }

        public int Compare(object a, object b)
        {
            var aval = field.GetValue(a);
            var bval = field.GetValue(b);
            var asComparable = aval as IComparable;
            if (asComparable != null)
            {
                var c = asComparable.CompareTo(bval);
                return (Direction == ListSortDirection.Ascending) ? c : -c;
            }
            asComparable = bval as IComparable;
            if (asComparable != null)
            {
                var c = asComparable.CompareTo(aval);
                return (Direction == ListSortDirection.Ascending) ? -c : c;
            }
            return 0;
        }
    }

    public class EntrySortDescriptions : List<EntrySortDescription>
    {
        public EntrySortDescriptions()
            : base()
        {
        }

        public EntrySortDescriptions(int capacity)
            : base(capacity)
        {
        }

        public int Compare(object a, object b)
        {
            foreach (var sort in this)
            {
                var cmp = sort.Compare(a, b);
                if (cmp != 0)
                    return cmp;
            }
            return 0;
        }
    }

    /// <summary>
    /// InfoSource entry sort description
    /// </summary>
    public class EntryFilter
    {
        private InfoField field;
        private object value;
        private ComparisonOperator op;

        public enum ComparisonOperator { Equal };

        public EntryFilter(InfoField field, object value)
        {
            this.op = ComparisonOperator.Equal;
            this.field = field;
            this.value = value;
        }

        public InfoField Field
        {
            get { return field; }
        }

        public object Value
        {
            get { return value; }
        }

        public ComparisonOperator Operator
        {
            get { return op; }
        }

        public bool Accepts(object obj)
        {
            Debug.Assert(Operator == ComparisonOperator.Equal);
            var objval = field.GetValue(obj);
            if (objval == value)
                return true;
            if (objval == null || value == null)
                return false;
            return objval.Equals(value);
        }
    }

    public class EntryFilters: List<EntryFilter>
    {
        public EntryFilters()
            : base()
        {
        }

        public EntryFilters(int capacity)
            : base(capacity)
        {
        }

        public bool Accepts(object obj)
        {
            foreach (var filter in this)
            {
                if (!filter.Accepts(obj))
                    return false;
            }
            return true;
        }
    }

    /// <summary>
    /// Internal interface to make possible of InfoSourceOwnedListOf<T> InfoSourceOwnedListDesignerControl<T>
    /// </summary>
    public interface IInfoSourceOwnedObject: IComponent
    {
        string Name { get; set; }

        string Title { get; set; }

        InfoSource Owner { get; }

        int Order { get; set; }

        void SetOwner(InfoSource value);
    }

    /// <summary>
    /// Base IInfoFieldGroupList implementation
    /// </summary>
    public class InfoSourceOwnedListOf<T> : List<T>, IList<T>, ICollection<T>
        where T: IInfoSourceOwnedObject
    {
        private Dictionary<string, T> byName;
        private InfoSource owner;

        public InfoSourceOwnedListOf(InfoSource owner)
        {
            this.owner = owner;
            if (!owner.IsBeingDesigned) // do not use the dictionary at design-time since item names may be changed by the programmer
                byName = new Dictionary<string, T>();
        }

        public new void Add(T item)
        {
            base.Add(item);
            item.SetOwner(owner);
            if (byName != null)
            {
                string fName = item.Name;
                if (!string.IsNullOrEmpty(fName))
                    byName.Add(item.Name, item);
            }
        }

        public new void Insert(int index, T item)
        {
            base.Insert(index, item);
            item.SetOwner(owner);
            if (byName != null)
            {
                string fName = item.Name;
                if (!string.IsNullOrEmpty(fName))
                    byName.Add(item.Name, item);
            }
        }

        public new bool Remove(T item)
        {
            if (item.Owner == owner)
                item.SetOwner(null);
            if (byName != null)
                byName.Remove(item.Name);
            return base.Remove(item);
        }

        public new void RemoveAt(int index)
        {
            var item = this[index];
            if (item.Owner == owner)
                item.SetOwner(null);
            if (byName != null)
                byName.Remove(item.Name);
            base.RemoveAt(index);
        }

        public void Move(int oldIndex, int newIndex)
        {
            var item = this[oldIndex];
            base.RemoveAt(oldIndex);
            base.Insert(newIndex, item);
        }

        // TODO: Add notification events here and use it at the InfoSourceContentsDesignerControl

        public new void Clear()
        {
            base.Clear();
            if (byName != null)
                byName.Clear();
        }

        public T this[string name]
        {
            get 
            {
                if (byName == null)
                {
                    byName = new Dictionary<string, T>();
                    foreach (var itm in this)
                        byName[itm.Name] = itm;
                }
                return byName[name];
            }
        }
    }
}

namespace InfFront.InfoSources.Base.Internal
{
    public static class InfoSourceExtensions
    {
    }
}